Exemplo: altura e peso
Pessoas mais altas tendem a ser mais pesadas?
Temos dados de \(80\) alunos e alunas, com alturas em cm e pesos em kg:
Correlação linear
Vamos subtrair, de cada peso, a média dos pesos; vamos subtrair, de cada altura, a média das alturas.
peso_altura <- peso_altura %>%
mutate(
altura_desvio = altura - mean(altura),
peso_desvio = peso - mean(peso)
)
O novo scatterplot tem a mesma forma. Só mudam as escalas:
peso_altura %>%
ggplot(aes(altura_desvio, peso_desvio)) +
geom_point() +
labs(
x = 'desvios altura (cm)',
y = 'desvios peso\n(kg)'
)

Quando o desvio da altura e o desvio do peso têm o mesmo sinal, os valores têm uma associação positiva: ou altura e peso estão ambos acima da média, ou ambos abaixo da média.
Colorindo os pontos de acordo com a associação:
peso_altura %>%
ggplot(aes(altura_desvio, peso_desvio)) +
geom_point(
data = . %>% filter(altura_desvio * peso_desvio >= 0),
color = 'blue'
) +
geom_point(
data = . %>% filter(altura_desvio * peso_desvio < 0),
color = 'red'
) +
geom_hline(aes(yintercept = 0), linetype = 'dashed') +
geom_vline(aes(xintercept = 0), linetype = 'dashed') +
labs(
x = 'desvios altura (cm)',
y = 'desvios peso\n(kg)'
)

Covariância
Examine o código acima: para verificar se os desvios têm o mesmo sinal, basta verificar se o produto dos desvios é positivo.
E mais: quanto maior o produto dos desvios, mais acima (ou mais abaixo) das respectivas médias os desvios estão, ao mesmo tempo. Ou seja, maior a associação entre as duas variáveis.
Estamos falando de
\[
\text{produto dos desvios}_i = (\text{altura}_i - E(\text{altura})) \cdot (\text{peso}_i - E(\text{peso}))
\]
Podemos calcular os produtos dos desvios e achar a média destes produtos. O resultado vai ser a covariância:
\[
\text{Cov}(\text{altura}, \text{peso}) =
\frac{
\sum\; [(\text{altura}_i - E(\text{altura})) \cdot (\text{peso}_i - E(\text{peso}))]
}{n}
\]
Ou, de forma mais compacta,
\[
\text{Cov}(\text{altura}, \text{peso}) =
E[(\text{altura} - E(\text{altura})) \cdot (\text{peso} - E(\text{peso}))]
\]
O R usa \(n - 1\) no denominador, em vez de \(n\). Isto significa que o R calcula a covariância amostral, que serve como um estimador da covariância populacional.
Calculando:
peso_altura %>%
mutate(produto = altura_desvio * peso_desvio) %>%
summarize(cov = sum(produto)/(nrow(.) - 1)) %>%
pull(cov)
## [1] 73,8935
Em R
cov(peso_altura$altura, peso_altura$peso)
## [1] 73,8935
Exercícios
Qual é a unidade da covariância? Examine as fórmulas acima para responder.
Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.
k <- 10
X <- 1:5
Y <- k * X
tibble(X, Y) %>%
ggplot(aes(X, Y)) +
geom_point(size = 2) +
labs(
title = paste('cov(X, Y) =', cov(X, Y))
)

Qual a covariância máxima entre duas variáveis? E a mínima?
Usando as propriedades do valor esperado, mostre que, para quaisquer variáveis aleatórias \(X\) e \(Y\)
\[
\text{Cov}(X,Y) \quad=\quad E\big[(X - E(X)) \cdot (Y - E(Y))\big] \quad=\quad E(XY) - E(X)E(Y)
\]
Lembre-se de que a variância de uma variável aleatória \(X\) pode ser escrita como
\[
\text{Var}(X) = E(X^2) - [E(X)]^2
\]
Alguma semelhança entre esta expressão e a expressão do item anterior?
Qual a covariância de uma variável aleatória \(X\) com ela mesma?
O R usa \(n - 1\) no denominador da covariância. Mostre que definir a covariância como \[
\text{Cov}(X, Y) = \frac{1}{n - 1} \cdot \sum_{i = 1}^n \big[(x_i - E(X)) \cdot (y_i - E(Y)) \big]
\]
é equivalente a
\[
\text{Cov}(X, Y) = \frac{n}{n-1} \cdot \big[ E(XY) - E(X)E(Y) \big]
\]
Usando o data frame peso_altura, compute o valor da expressão acima e compare com o resultado de cov(peso_altura$altura, peso_altura$peso).
Coeficiente de correlação linear
Vamos padronizar as alturas e pesos (subtrair a média e dividir pelo desvio padrão):
peso_altura <- peso_altura %>%
mutate(
altura_padronizada = scale(altura),
peso_padronizado = scale(peso)
)
E construir o scatterplot:
peso_altura %>%
ggplot(aes(altura_padronizada, peso_padronizado)) +
geom_point(
data = . %>% filter(altura_padronizada * peso_padronizado >= 0),
color = 'blue'
) +
geom_point(
data = . %>% filter(altura_padronizada * peso_padronizado < 0),
color = 'red'
) +
geom_hline(aes(yintercept = 0), linetype = 'dashed') +
geom_vline(aes(xintercept = 0), linetype = 'dashed') +
labs(
x = 'altura padronizada',
y = 'peso\npadronizado'
)

Ou seja, agora as variáveis são
\[
Z_A = \frac{\text{altura} - E(\text{altura})}{DP(\text{altura})}
\]
e
\[
Z_P = \frac{\text{peso} - E(\text{peso})}{DP(\text{peso})}
\]
A covariância entre elas é
cov(peso_altura$peso_padronizado, peso_altura$altura_padronizada)
## [,1]
## [1,] 0,6440311
Quando padronizamos as duas variáveis, a covariância entre elas se chama correlação.
O coeficiente de correlação amostral é este valor.
\[
r = \frac{\sum x_i y_i}{n - 1}
\]
onde \(X\) e \(Y\) são variáveis padronizadas.
Alguns livros chamam o coeficiente de correlação de \(\rho\) em vez de \(r\).
O coeficiente de correlação sempre está entre \(-1\) e \(1\), inclusive.
Fórmulas alternativas para o coeficiente de correlação entre \(X\) e \(Y\) (não necessariamente padronizadas):
\[
r = \frac{\text{Cov}(X, Y)}{DP_X \cdot DP_Y}
\]
e
\[
r = \frac{
\sum(x_i - E(X))\cdot(y_i - E(Y))
}{
\sqrt{\sum(x_i - E(X))^2\cdot(y_i - E(Y))^2}
}
\]
Em R
cor(peso_altura$peso, peso_altura$altura)
## [1] 0,6440311
Exercícios
Qual a unidade do coeficiente de correlação?
Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.
k <- 10
X <- 1:5
Y <- k * X
tibble(X, Y) %>%
ggplot(aes(X, Y)) +
geom_point(size = 2) +
labs(
title = paste('cor(X, Y) =', cor(X, Y))
)

Como você explica os valores de \(\text{cor}(X,Y)\) no item anterior, em comparação com os valores de \(\text{cov}(X,Y)\) nesse outro exercício?
Qual a correlação de uma variável aleatória \(X\) com ela mesma?
Usando o data frame peso_altura, compute o valor do coeficiente de correlação entre peso e altura usando as fórmulas alternativas.
Observações importantes
A correlação é linear

Se \(r > 0\), valores altos de uma variável tendem a corresponder a valores altos da outra variável.
Se \(r < 0\), valores altos de uma variável tendem a corresponder a valores baixos da outra variável.
Se \(r = 0\), não há correlação linear entre as variáveis.
O coeficiente de correlação \(r\) só mede a correlação linear entre duas variáveis.

Correlação \(\neq\) causação
Dados coletados em Oldenburg, na Alemanha, nos anos \(1930\), mostrando a correlação entre a quantidade de cegonhas e a população (de pessoas) na cidade:
df <- read_csv('data/Storks.csv') %>%
transmute(
cegonhas = Storks,
pessoas = Population
)
r <- cor(df$cegonhas, df$pessoas) %>% round(2)
df %>%
ggplot(aes(cegonhas, pessoas)) +
geom_point() +
labs(
title = paste('r =', r)
)

Variável oculta: quantidade de casas com chaminé.
Ou coincidência: http://tylervigen.com/spurious-correlations
Teste e intervalo de confiança para a correlação
Exemplo: PIB e CO\(_2\)
Em uma amostra de \(10\) países, examinamos o PIB (em trilhões de dólares) e a quantidade de emissões de CO\(_2\) (em milhões de toneladas):
Gráfico:
df %>%
ggplot(aes(PIB, emissões)) +
geom_point() +
scale_y_continuous(
'emissões\n(milhões de\ntoneladas)',
breaks = seq(0, 1200, 200),
limits = c(0, 1200)
) +
scale_x_continuous(
'PIB (trilhões US$)',
breaks = 0:6,
limits = c(0, 6)
)

O coeficiente de correlação amostral é
## [1] 0,9115924
Como esta é uma amostra, devemos calcular um intervalo de confiança para o coeficiente de correlação populacional.
O R faz isto com a função cor.test:
ct <- cor.test(df$PIB, df$emissões)
ct
##
## Pearson's product-moment correlation
##
## data: df$PIB and df$emissões
## t = 6,272, df = 8, p-value = 0,0002399
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0,6618340 0,9791965
## sample estimates:
## cor
## 0,9115924
Conclusão: com \(95\%\) de confiança, estimamos que o coeficiente de correlação populacional é capturado pelo intervalo
\[
[\quad 0{,}66 \quad ;\quad 0{,}98 \quad]
\]
Exercícios
Qual é o tipo de teste usado por cor.test?
Quais são as hipóteses do teste?
Como é calculado o valor da estatística do teste (\(6{,}27\) no exemplo acima)?
Como é calculado o valor \(p\) (\(0{,}00\) no exemplo acima)?
Implemente uma função em R para fazer o mesmo que cor.test.
LS0tCnRpdGxlOiAnQ29ycmVsYcOnw6NvIGVudHJlIGR1YXMgdmFyacOhdmVpcycKc3VidGl0bGU6ICdQcm9iZXN0JwphdXRob3I6ICdmbmF1ZmVsJwplbWFpbDogJ2h0dHBzOi8vZm5hdWZlbC5naXRodWIuaW8vJwpkYXRlOiAnICh2LiBgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVkLyVtLyVZIilgKScKbGFuZzogJ3B0JwpvdXRwdXQ6IHJtZGZvcm1hdDo6Zm5hdWZlbF9ybWRfZm9ybWF0Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoa25pdHIpCgpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgCiAgIyBjb2xsYXBzZSA9IFRSVUUsCiAgIyBjYWNoZSA9IFRSVUUsCiAgb3V0LndpZHRoID0gIjkwJSIsCiAgZmlnLmFsaWduID0gJ2NlbnRlcicsCiAgZmlnLndpZHRoID0gNywKICBmaWcuc2hvdyA9ICJob2xkIgopCgojIFN1cHJlc3MgY3JheW9uIG91dHB1dApvcHRpb25zKGNyYXlvbi5lbmFibGVkID0gRkFMU0UpCgpvcHRpb25zKAogICMgQXZvaWQgc2NpZW50aWZpYyBub3RhdGlvbgogIHNjaXBlbiA9IDE1LAogICMgVXNlIGEgY29tbWEgYXMgZGVjaW1hbCBzZXBhcmF0b3IKICBPdXREZWMgPSAnLCcsCiAgIyBOdW1iZXIgb2YgZGVjaW1hbCBkaWdpdHMgZm9yIG51bWJlcnMgcHJvZHVjZWQgYnkgaW5saW5lIFIgY29kZQogIGZtZGlnaXRzID0gMgopCgojIFVzZWZ1bCBsaWJyYXJpZXMKbGlicmFyeShnbHVlKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShsYXRleDJleHApCmxpYnJhcnkoa2FibGVFeHRyYSkKb3B0aW9ucyhrbml0ci5rYWJsZS5OQSA9ICcnKQoKIyBGb3IgbmljZSBkYXRhZnJhbWUgc3VtbWFyaWVzCmxpYnJhcnkoc3VtbWFyeXRvb2xzKQpzdF9vcHRpb25zKAogIHBsYWluLmFzY2lpID0gRkFMU0UsCiAgZGZTdW1tYXJ5LnZhcm51bWJlcnMgPSBGQUxTRSwKICBkZlN1bW1hcnkuc3R5bGUgPSAnZ3JpZCcsCiAgZGZTdW1tYXJ5LmdyYXBoLm1hZ25pZiA9IC43NQopCgojIFRpZHkhCmxpYnJhcnkodGlkeXZlcnNlKQoKIyBVc2VmdWwgZnVuY3Rpb25zIHByb3ZpZGVkIGJ5IHRoZSBybWRmb3JtYXQgcGFja2FnZQojIAojIEV4ZWN1dGUgCiMgCiMgICBjYXQoc3lzdGVtLmZpbGUoInJtYXJrZG93bi9yZXNvdXJjZXMvY29tbW9uLlIiLCBwYWNrYWdlID0gInJtZGZvcm1hdCIpKQojIAojIHRvIHNlZSB0aGUgbG9jYXRpb24gb2YgdGhlIGZpbGUKc291cmNlKAogIHN5c3RlbS5maWxlKAogICAgInJtYXJrZG93bi9yZXNvdXJjZXMvY29tbW9uLlIiLAogICAgcGFja2FnZSA9ICJybWRmb3JtYXQiCiAgKQopCmBgYAoKCgojIEV4ZW1wbG86IGFsdHVyYSBlIHBlc28KCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPVRSVUV9CnBlc29fYWx0dXJhIDwtIHJlYWRfY3N2KCdkYXRhL0hlaWdodHNfYW5kX1dlaWdodHMuY3N2JykgJT4lIAogIG11dGF0ZSgKICAgIGFsdHVyYSA9IEhlaWdodCAqIDIuNTQsCiAgICBwZXNvID0gV2VpZ2h0ICogMC40NQogICkKYGBgCgo6Ojogey5ybWRib3h9CgpQZXNzb2FzIG1haXMgYWx0YXMgdGVuZGVtIGEgc2VyIG1haXMgcGVzYWRhcz8KClRlbW9zIGRhZG9zIGRlICRgciBucm93KHBlc29fYWx0dXJhKWAkIGFsdW5vcyBlIGFsdW5hcywgY29tIGFsdHVyYXMgZW0gY20gZSBwZXNvcyBlbSBrZzoKCmBgYHtyIGVjaG89RkFMU0V9CnBlc29fYWx0dXJhICU+JSBzZWxlY3QoYWx0dXJhLCBwZXNvKQpgYGAKCjo6OgoKKiBPIGdyw6FmaWNvIGRlIGRpc3BlcnPDo28gKCpzY2F0dGVycGxvdCopIMOpIG8gaWRlYWwgcGFyYSB2aXN1YWxpemFyIGEgY29ycmVsYcOnw6NvIGVudHJlIGR1YXMgdmFyacOhdmVpcyBudW3DqXJpY2FzOgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFsdHVyYSwgcGVzbykpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB4ID0gJ2FsdHVyYSAoY20pJywKICAgICAgICAgIHkgPSAncGVzb1xuKGtnKScKICAgICAgICApCiAgICBgYGAKCgojIENvcnJlbGHDp8OjbyBsaW5lYXIKCiogVmFtb3Mgc3VidHJhaXIsIGRlIGNhZGEgcGVzbywgYSBtw6lkaWEgZG9zIHBlc29zOyB2YW1vcyBzdWJ0cmFpciwgZGUgY2FkYSBhbHR1cmEsIGEgbcOpZGlhIGRhcyBhbHR1cmFzLgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgPC0gcGVzb19hbHR1cmEgJT4lIAogICAgICBtdXRhdGUoCiAgICAgICAgYWx0dXJhX2Rlc3ZpbyA9IGFsdHVyYSAtIG1lYW4oYWx0dXJhKSwKICAgICAgICBwZXNvX2Rlc3ZpbyA9IHBlc28gLSBtZWFuKHBlc28pCiAgICAgICkKICAgIGBgYAoKKiBPIG5vdm8gKnNjYXR0ZXJwbG90KiB0ZW0gYSBtZXNtYSBmb3JtYS4gU8OzIG11ZGFtIGFzIGVzY2FsYXM6CgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIGdncGxvdChhZXMoYWx0dXJhX2Rlc3ZpbywgcGVzb19kZXN2aW8pKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeCA9ICdkZXN2aW9zIGFsdHVyYSAoY20pJywKICAgICAgICAgIHkgPSAnZGVzdmlvcyBwZXNvXG4oa2cpJwogICAgICAgICkKICAgIGBgYAoKKiBRdWFuZG8gbyBkZXN2aW8gZGEgYWx0dXJhIGUgbyBkZXN2aW8gZG8gcGVzbyB0w6ptIG8gW21lc21vIHNpbmFsXXsuaGx9LCBvcyB2YWxvcmVzIHTDqm0gdW1hIFthc3NvY2lhw6fDo28gcG9zaXRpdmFdey5obH06IG91IGFsdHVyYSBlIHBlc28gZXN0w6NvIGFtYm9zIGFjaW1hIGRhIG3DqWRpYSwgb3UgYW1ib3MgYWJhaXhvIGRhIG3DqWRpYS4KCiogQ29sb3JpbmRvIG9zIHBvbnRvcyBkZSBhY29yZG8gY29tIGEgYXNzb2NpYcOnw6NvOgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFsdHVyYV9kZXN2aW8sIHBlc29fZGVzdmlvKSkgKwogICAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICBkYXRhID0gLiAlPiUgZmlsdGVyKGFsdHVyYV9kZXN2aW8gKiBwZXNvX2Rlc3ZpbyA+PSAwKSwKICAgICAgICAgIGNvbG9yID0gJ2JsdWUnCiAgICAgICAgKSArCiAgICAgICAgZ2VvbV9wb2ludCgKICAgICAgICAgIGRhdGEgPSAuICU+JSBmaWx0ZXIoYWx0dXJhX2Rlc3ZpbyAqIHBlc29fZGVzdmlvIDwgMCksCiAgICAgICAgICBjb2xvciA9ICdyZWQnCiAgICAgICAgKSArCiAgICAgICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDApLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgICAgICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDApLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHggPSAnZGVzdmlvcyBhbHR1cmEgKGNtKScsCiAgICAgICAgICB5ID0gJ2Rlc3Zpb3MgcGVzb1xuKGtnKScKICAgICAgICApCiAgICBgYGAKCiMgQ292YXJpw6JuY2lhCgoqIEV4YW1pbmUgbyBjw7NkaWdvIGFjaW1hOiBwYXJhIHZlcmlmaWNhciBzZSBvcyBkZXN2aW9zIHTDqm0gbyBtZXNtbyBzaW5hbCwgYmFzdGEgdmVyaWZpY2FyIHNlIG8gcHJvZHV0byBkb3MgZGVzdmlvcyDDqSBwb3NpdGl2by4KCiogRSBtYWlzOiBxdWFudG8gbWFpb3IgbyBwcm9kdXRvIGRvcyBkZXN2aW9zLCBtYWlzIGFjaW1hIChvdSBtYWlzIGFiYWl4bykgZGFzIHJlc3BlY3RpdmFzIG3DqWRpYXMgb3MgZGVzdmlvcyBlc3TDo28sIFthbyBtZXNtbyB0ZW1wb117LmhsfS4gT3Ugc2VqYSwgbWFpb3IgYSBhc3NvY2lhw6fDo28gZW50cmUgYXMgZHVhcyB2YXJpw6F2ZWlzLgoKKiBFc3RhbW9zIGZhbGFuZG8gZGUKCiAgJCQKICBcdGV4dHtwcm9kdXRvIGRvcyBkZXN2aW9zfV9pID0gKFx0ZXh0e2FsdHVyYX1faSAtIEUoXHRleHR7YWx0dXJhfSkpIFxjZG90IChcdGV4dHtwZXNvfV9pIC0gRShcdGV4dHtwZXNvfSkpCiAgJCQKCiogUG9kZW1vcyBjYWxjdWxhciBvcyBwcm9kdXRvcyBkb3MgZGVzdmlvcyBlIGFjaGFyIGEgbcOpZGlhIGRlc3RlcyBwcm9kdXRvcy4gTyByZXN1bHRhZG8gdmFpIHNlciBhIFtjb3ZhcmnDom5jaWE6XXsuaGx9CgogICQkCiAgXHRleHR7Q292fShcdGV4dHthbHR1cmF9LCBcdGV4dHtwZXNvfSkgPSAKICBcZnJhY3sKICBcc3VtXDsgWyhcdGV4dHthbHR1cmF9X2kgLSBFKFx0ZXh0e2FsdHVyYX0pKSBcY2RvdCAoXHRleHR7cGVzb31faSAtIEUoXHRleHR7cGVzb30pKV0KICB9e259CiAgJCQKCiogT3UsIGRlIGZvcm1hIG1haXMgY29tcGFjdGEsCgogICQkCiAgXHRleHR7Q292fShcdGV4dHthbHR1cmF9LCBcdGV4dHtwZXNvfSkgPSAKICBFWyhcdGV4dHthbHR1cmF9IC0gRShcdGV4dHthbHR1cmF9KSkgXGNkb3QgKFx0ZXh0e3Blc299IC0gRShcdGV4dHtwZXNvfSkpXQogICQkCgoqIFtPIFIgdXNhICRuIC0gMSQgbm8gZGVub21pbmFkb3IsXXsuaGx9IGVtIHZleiBkZSAkbiQuIElzdG8gc2lnbmlmaWNhIHF1ZSBvIFIgY2FsY3VsYSBhIFtjb3ZhcmnDom5jaWEgYW1vc3RyYWxdey5obH0sIHF1ZSBzZXJ2ZSBjb21vIHVtIGVzdGltYWRvciBkYSBjb3ZhcmnDom5jaWEgcG9wdWxhY2lvbmFsLgoKKiBDYWxjdWxhbmRvOgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgJT4lIAogICAgICBtdXRhdGUocHJvZHV0byA9IGFsdHVyYV9kZXN2aW8gKiBwZXNvX2Rlc3ZpbykgJT4lIAogICAgICBzdW1tYXJpemUoY292ID0gc3VtKHByb2R1dG8pLyhucm93KC4pIC0gMSkpICU+JSAKICAgICAgcHVsbChjb3YpCiAgICBgYGAKCgojIyBFbSBSCgpgYGB7cn0KY292KHBlc29fYWx0dXJhJGFsdHVyYSwgcGVzb19hbHR1cmEkcGVzbykKYGBgCgojIyBFeGVyY8OtY2lvcwoKKiBRdWFsIMOpIGEgdW5pZGFkZSBkYSBjb3ZhcmnDom5jaWE/IEV4YW1pbmUgYXMgZsOzcm11bGFzIGFjaW1hIHBhcmEgcmVzcG9uZGVyLgoKKiBbXXsjZXhjb3Z9IEV4ZWN1dGUgbyBibG9jbyBhYmFpeG8gY29tIGRpdmVyc29zIHZhbG9yZXMgZGUgYGtgLCBpbmNsdXNpdmUgdmFsb3JlcyBuZWdhdGl2b3MuCgogICAgYGBge3J9CiAgICBrIDwtIDEwCiAgICAKICAgIFggPC0gMTo1CiAgICBZIDwtIGsgKiBYCiAgICAKICAgIHRpYmJsZShYLCBZKSAlPiUgCiAgICAgIGdncGxvdChhZXMoWCwgWSkpICsKICAgICAgICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHRpdGxlID0gcGFzdGUoJ2NvdihYLCBZKSA9JywgY292KFgsIFkpKQogICAgICAgICkKICAgIGBgYAoKKiBRdWFsIGEgY292YXJpw6JuY2lhIG3DoXhpbWEgZW50cmUgZHVhcyB2YXJpw6F2ZWlzPyBFIGEgbcOtbmltYT8KCiogVXNhbmRvIGFzIHByb3ByaWVkYWRlcyBkbyB2YWxvciBlc3BlcmFkbywgbW9zdHJlIHF1ZSwgcGFyYSBxdWFpc3F1ZXIgdmFyacOhdmVpcyBhbGVhdMOzcmlhcyAkWCQgZSAkWSQKCiAgJCQKICBcdGV4dHtDb3Z9KFgsWSkgXHF1YWQ9XHF1YWQgRVxiaWdbKFggLSBFKFgpKSBcY2RvdCAoWSAtIEUoWSkpXGJpZ10gXHF1YWQ9XHF1YWQgRShYWSkgLSBFKFgpRShZKQogICQkCgoqIExlbWJyZS1zZSBkZSBxdWUgYSB2YXJpw6JuY2lhIGRlIHVtYSB2YXJpw6F2ZWwgYWxlYXTDs3JpYSAkWCQgcG9kZSBzZXIgZXNjcml0YSBjb21vCgogICQkCiAgXHRleHR7VmFyfShYKSA9IEUoWF4yKSAtIFtFKFgpXV4yCiAgJCQKCiAgQWxndW1hIHNlbWVsaGFuw6dhIGVudHJlIGVzdGEgZXhwcmVzc8OjbyBlIGEgZXhwcmVzc8OjbyBkbyBpdGVtIGFudGVyaW9yPwogIAoqIFF1YWwgYSBjb3ZhcmnDom5jaWEgZGUgdW1hIHZhcmnDoXZlbCBhbGVhdMOzcmlhICRYJCBjb20gZWxhIG1lc21hPwoKKiBPIFIgdXNhICRuIC0gMSQgbm8gZGVub21pbmFkb3IgZGEgY292YXJpw6JuY2lhLiBNb3N0cmUgcXVlIGRlZmluaXIgYSBjb3ZhcmnDom5jaWEgY29tbyAKICAkJAogIFx0ZXh0e0Nvdn0oWCwgWSkgPSBcZnJhY3sxfXtuIC0gMX0gXGNkb3QgXHN1bV97aSA9IDF9Xm4gXGJpZ1soeF9pIC0gRShYKSkgXGNkb3QgKHlfaSAtIEUoWSkpIFxiaWddCiAgJCQKCiAgw6kgZXF1aXZhbGVudGUgYQogIAogICQkCiAgXHRleHR7Q292fShYLCBZKSA9IFxmcmFje259e24tMX0gXGNkb3QgXGJpZ1sgRShYWSkgLSBFKFgpRShZKSBcYmlnXQogICQkCgoKKiBVc2FuZG8gbyAqZGF0YSBmcmFtZSogYHBlc29fYWx0dXJhYCwgY29tcHV0ZSBvIHZhbG9yIGRhIGV4cHJlc3PDo28gYWNpbWEgZSBjb21wYXJlIGNvbSBvIHJlc3VsdGFkbyBkZSBgY292KHBlc29fYWx0dXJhJGFsdHVyYSwgcGVzb19hbHR1cmEkcGVzbylgLgoKCiMgQ29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGxpbmVhcgoKKiBWYW1vcyBwYWRyb25pemFyIGFzIGFsdHVyYXMgZSBwZXNvcyAoc3VidHJhaXIgYSBtw6lkaWEgZSBkaXZpZGlyIHBlbG8gZGVzdmlvIHBhZHLDo28pOgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgPC0gcGVzb19hbHR1cmEgJT4lIAogICAgICBtdXRhdGUoCiAgICAgICAgYWx0dXJhX3BhZHJvbml6YWRhID0gc2NhbGUoYWx0dXJhKSwKICAgICAgICBwZXNvX3BhZHJvbml6YWRvID0gc2NhbGUocGVzbykKICAgICAgKQogICAgYGBgCgoqIEUgY29uc3RydWlyIG8gKnNjYXR0ZXJwbG90KjoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgZ2dwbG90KGFlcyhhbHR1cmFfcGFkcm9uaXphZGEsIHBlc29fcGFkcm9uaXphZG8pKSArCiAgICAgICAgZ2VvbV9wb2ludCgKICAgICAgICAgIGRhdGEgPSAuICU+JSBmaWx0ZXIoYWx0dXJhX3BhZHJvbml6YWRhICogcGVzb19wYWRyb25pemFkbyA+PSAwKSwKICAgICAgICAgIGNvbG9yID0gJ2JsdWUnCiAgICAgICAgKSArCiAgICAgICAgZ2VvbV9wb2ludCgKICAgICAgICAgIGRhdGEgPSAuICU+JSBmaWx0ZXIoYWx0dXJhX3BhZHJvbml6YWRhICogcGVzb19wYWRyb25pemFkbyA8IDApLAogICAgICAgICAgY29sb3IgPSAncmVkJwogICAgICAgICkgKwogICAgICAgIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogICAgICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAwKSwgbGluZXR5cGUgPSAnZGFzaGVkJykgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB4ID0gJ2FsdHVyYSBwYWRyb25pemFkYScsCiAgICAgICAgICB5ID0gJ3Blc29cbnBhZHJvbml6YWRvJwogICAgICAgICkKICAgIGBgYAoKKiBPdSBzZWphLCBhZ29yYSBhcyB2YXJpw6F2ZWlzIHPDo28KCiAgJCQKICBaX0EgPSBcZnJhY3tcdGV4dHthbHR1cmF9IC0gRShcdGV4dHthbHR1cmF9KX17RFAoXHRleHR7YWx0dXJhfSl9CiAgJCQKCiAgZQogIAogICQkCiAgWl9QID0gXGZyYWN7XHRleHR7cGVzb30gLSBFKFx0ZXh0e3Blc299KX17RFAoXHRleHR7cGVzb30pfQogICQkCgoqIEEgY292YXJpw6JuY2lhIGVudHJlIGVsYXMgw6kKCiAgICBgYGB7cn0KICAgIGNvdihwZXNvX2FsdHVyYSRwZXNvX3BhZHJvbml6YWRvLCBwZXNvX2FsdHVyYSRhbHR1cmFfcGFkcm9uaXphZGEpCiAgICBgYGAKCiogUXVhbmRvIHBhZHJvbml6YW1vcyBhcyBkdWFzIHZhcmnDoXZlaXMsIGEgY292YXJpw6JuY2lhIGVudHJlIGVsYXMgc2UgY2hhbWEgW2NvcnJlbGHDp8Ojb117LmhsfS4KCiogTyBbY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIGFtb3N0cmFsXXsuaGx9IMOpIGVzdGUgdmFsb3IuCgogICQkCiAgciA9IFxmcmFje1xzdW0geF9pIHlfaX17biAtIDF9CiAgJCQKCiAgb25kZSBbJFgkIGUgJFkkIHPDo28gdmFyacOhdmVpcyBwYWRyb25pemFkYXNdey5obH0uCiAgCiogQWxndW5zIGxpdnJvcyBjaGFtYW0gbyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gZGUgJFxyaG8kIGVtIHZleiBkZSAkciQuCgoqIE8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIFtzZW1wcmUgZXN0w6EgZW50cmUgJC0xJCBlICQxJF17LmhsfSwgaW5jbHVzaXZlLgoKKiBGw7NybXVsYXMgYWx0ZXJuYXRpdmFzIHBhcmEgbyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gZW50cmUgJFgkIGUgJFkkIChuw6NvIG5lY2Vzc2FyaWFtZW50ZSBwYWRyb25pemFkYXMpOgoKICAkJAogIHIgPSBcZnJhY3tcdGV4dHtDb3Z9KFgsIFkpfXtEUF9YIFxjZG90IERQX1l9CiAgJCQKICAKICBlCgogICQkCiAgciA9IFxmcmFjewogICAgXHN1bSh4X2kgLSBFKFgpKVxjZG90KHlfaSAtIEUoWSkpCiAgfXsKICAgIFxzcXJ0e1xzdW0oeF9pIC0gRShYKSleMlxjZG90KHlfaSAtIEUoWSkpXjJ9CiAgfQogICQkCgoKIyMgRW0gUgoKYGBge3J9CmNvcihwZXNvX2FsdHVyYSRwZXNvLCBwZXNvX2FsdHVyYSRhbHR1cmEpCmBgYAoKIyMgRXhlcmPDrWNpb3MKCiogUXVhbCBhIHVuaWRhZGUgZG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvPwoKKiBFeGVjdXRlIG8gYmxvY28gYWJhaXhvIGNvbSBkaXZlcnNvcyB2YWxvcmVzIGRlIGBrYCwgaW5jbHVzaXZlIHZhbG9yZXMgbmVnYXRpdm9zLgoKICAgIGBgYHtyfQogICAgayA8LSAxMAogICAgCiAgICBYIDwtIDE6NQogICAgWSA8LSBrICogWAogICAgCiAgICB0aWJibGUoWCwgWSkgJT4lIAogICAgICBnZ3Bsb3QoYWVzKFgsIFkpKSArCiAgICAgICAgZ2VvbV9wb2ludChzaXplID0gMikgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB0aXRsZSA9IHBhc3RlKCdjb3IoWCwgWSkgPScsIGNvcihYLCBZKSkKICAgICAgICApCiAgICBgYGAKCiogQ29tbyB2b2PDqiBleHBsaWNhIG9zIHZhbG9yZXMgZGUgJFx0ZXh0e2Nvcn0oWCxZKSQgbm8gaXRlbSBhbnRlcmlvciwgZW0gY29tcGFyYcOnw6NvIGNvbSBvcyB2YWxvcmVzIGRlICRcdGV4dHtjb3Z9KFgsWSkkIFtuZXNzZSBvdXRybyBleGVyY8OtY2lvXSgjZXhjb3YpPwoKKiBRdWFsIGEgY29ycmVsYcOnw6NvIGRlIHVtYSB2YXJpw6F2ZWwgYWxlYXTDs3JpYSAkWCQgY29tIGVsYSBtZXNtYT8KCiogVXNhbmRvIG8gKmRhdGEgZnJhbWUqIGBwZXNvX2FsdHVyYWAsIGNvbXB1dGUgbyB2YWxvciBkbyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gZW50cmUgcGVzbyBlIGFsdHVyYSB1c2FuZG8gYXMgZsOzcm11bGFzIGFsdGVybmF0aXZhcy4KCgojIyBPYnNlcnZhw6fDtWVzIGltcG9ydGFudGVzCgojIyMgQSBjb3JyZWxhw6fDo28gw6kgKmxpbmVhciogey19CgohW10oaW1hZ2VzL1BlYXJzb25fQ29ycmVsYXRpb25fQ29lZmZpY2llbnRfYW5kX2Fzc29jaWF0ZWRfc2NhdHRlcnBsb3RzLnBuZyl7IHN0eWxlPSJ3aWR0aDogOTAlOyIgLmNlbnRlciB9Cgo6Ojp7c3R5bGU9InRleHQtYWxpZ246IHJpZ2h0OyJ9CihodHRwczovL2NvbW1vbnMud2lraW1lZGlhLm9yZy93aWtpL0ZpbGU6UGVhcnNvbl9Db3JyZWxhdGlvbl9Db2VmZmljaWVudF9hbmRfYXNzb2NpYXRlZF9zY2F0dGVycGxvdHMucG5nKQo6OjoKCiogU2UgJHIgPiAwJCwgdmFsb3JlcyBbYWx0b3Ndey5obH0gZGUgdW1hIHZhcmnDoXZlbCB0ZW5kZW0gYSBjb3JyZXNwb25kZXIgYSB2YWxvcmVzIFthbHRvc117LmhsfSBkYSBvdXRyYSB2YXJpw6F2ZWwuCgoqIFNlICRyIDwgMCQsIHZhbG9yZXMgW2FsdG9zXXsuaGx9IGRlIHVtYSB2YXJpw6F2ZWwgdGVuZGVtIGEgY29ycmVzcG9uZGVyIGEgdmFsb3JlcyBbYmFpeG9zXXsuaGx9IGRhIG91dHJhIHZhcmnDoXZlbC4KCiogU2UgJHIgPSAwJCwgbsOjbyBow6EgY29ycmVsYcOnw6NvIFtsaW5lYXJdey5obH0gZW50cmUgYXMgdmFyacOhdmVpcy4KCiogTyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gJHIkIHPDsyBtZWRlIGEgY29ycmVsYcOnw6NvIFtsaW5lYXJdey5obH0gZW50cmUgZHVhcyB2YXJpw6F2ZWlzLgoKICAhW10oaW1hZ2VzL0NvcnJlbGF0aW9uX2V4YW1wbGVzMi5zdmcpeyBzdHlsZT0id2lkdGg6IDkwJTsiIC5jZW50ZXIgfQoKOjo6e3N0eWxlPSJ0ZXh0LWFsaWduOiByaWdodDsifQooaHR0cHM6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOkNvcnJlbGF0aW9uX2V4YW1wbGVzMi5zdmcpCjo6OgoKKiBbRXhlbXBsb3MgZGUgQW5zY29tYmVdKGh0dHBzOi8vcHQud2lraXBlZGlhLm9yZy93aWtpL1F1YXJ0ZXRvX2RlX0Fuc2NvbWJlKSwgY29tICQ0JCBjb25qdW50b3MgZGUgZGFkb3MgY29tIFtvIG1lc21vIHZhbG9yIGRlICRyJF17LmhsfSBtYXMgY29tIGFzc29jaWHDp8O1ZXMgW211aXRvIGRpZmVyZW50ZXNdey5obH0gZW50cmUgYXMgdmFyacOhdmVpczoKCiAgYGBge3J9CiAgYW5zY29tYmUKICBgYGAKCiAgICBgYGB7ciBtZXNzYWdlPUZBTFNFLCBjYWNoZT1UUlVFfQogICAgZGVzZW5oYXIgPC0gZnVuY3Rpb24obikgewogICAgICAKICAgICAgbm9tZXggPC0gcGFzdGUwKCd4JywgbikKICAgICAgbm9tZXkgPC0gcGFzdGUwKCd5JywgbikKICAgICAgciA8LSBjb3IoYW5zY29tYmVbW25vbWV4XV0sIGFuc2NvbWJlW1tub21leV1dKSAlPiUgcm91bmQoMikKICAgICAgCiAgICAgIGFuc2NvbWJlICU+JSAKICAgICAgICBnZ3Bsb3QoYWVzKC5kYXRhW1tub21leF1dLCAuZGF0YVtbbm9tZXldXSkpICsKICAgICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzZSA9IEZBTFNFKSArCiAgICAgICAgICBsYWJzKAogICAgICAgICAgICB0aXRsZSA9IHBhc3RlKCdyID0nLCByKQogICAgICAgICAgKQogICAgICAKICAgIH0KICAgICAgCiAgICAgIAogICAgcGxvdHMgPC0gbWFwKDE6NCwgZGVzZW5oYXIpCiAgICBwbG90c1tbMV1dICsgcGxvdHNbWzJdXSArIHBsb3RzW1szXV0gKyBwbG90c1tbNF1dIAogICAgYGBgCgoqIENvbmNsdXPDtWVzOgoKICAqIE8gdmFsb3IgZGUgJHIkIHPDsyByZWZsZXRlIGNvcnJlbGHDp8OjbyBbbGluZWFyXXsuaGx9LgogIAogICogQSBwcmVzZW7Dp2EgZGUgWypvdXRsaWVycypdey5obH0gcHJvZHV6IHZhbG9yZXMgbsOjby1jb25macOhdmVpcyBkZSAkciQuCiAgCiAgCiMjIyBDb3JyZWxhw6fDo28gJFxuZXEkIGNhdXNhw6fDo28gey19CgoqIERhZG9zIGNvbGV0YWRvcyBlbSBPbGRlbmJ1cmcsIG5hIEFsZW1hbmhhLCBub3MgYW5vcyAkMTkzMCQsIG1vc3RyYW5kbyBhIGNvcnJlbGHDp8OjbyBlbnRyZSBhIHF1YW50aWRhZGUgZGUgY2Vnb25oYXMgZSBhIHBvcHVsYcOnw6NvIChkZSBwZXNzb2FzKSBuYSBjaWRhZGU6CgogICAgYGBge3IgbWVzc2FnZT1GQUxTRSwgY2FjaGU9VFJVRX0KICAgIGRmIDwtIHJlYWRfY3N2KCdkYXRhL1N0b3Jrcy5jc3YnKSAlPiUgCiAgICAgIHRyYW5zbXV0ZSgKICAgICAgICBjZWdvbmhhcyA9IFN0b3JrcywKICAgICAgICBwZXNzb2FzID0gUG9wdWxhdGlvbgogICAgICApIAogICAgCiAgICByIDwtIGNvcihkZiRjZWdvbmhhcywgZGYkcGVzc29hcykgJT4lIHJvdW5kKDIpCiAgICAKICAgIGRmICU+JSAKICAgICAgZ2dwbG90KGFlcyhjZWdvbmhhcywgcGVzc29hcykpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB0aXRsZSA9IHBhc3RlKCdyID0nLCByKQogICAgICAgICkKICAgIAogICAgYGBgCgoqIFZhcmnDoXZlbCBvY3VsdGE6IHF1YW50aWRhZGUgZGUgY2FzYXMgY29tIGNoYW1pbsOpLgoKKiBPdSBjb2luY2lkw6puY2lhOiBodHRwOi8vdHlsZXJ2aWdlbi5jb20vc3B1cmlvdXMtY29ycmVsYXRpb25zCgoKIyBUZXN0ZSBlIGludGVydmFsbyBkZSBjb25maWFuw6dhIHBhcmEgYSBjb3JyZWxhw6fDo28KCiMjIEV4ZW1wbG86IFBJQiBlIENPJF8yJAoKKiBFbSB1bWEgYW1vc3RyYSBkZSAkMTAkIHBhw61zZXMsIGV4YW1pbmFtb3MgbyBQSUIgKGVtIHRyaWxow7VlcyBkZSBkw7NsYXJlcykgZSBhIHF1YW50aWRhZGUgZGUgZW1pc3PDtWVzIGRlIENPJF8yJCAoZW0gbWlsaMO1ZXMgZGUgdG9uZWxhZGFzKToKCiAgYGBge3IgZWNobz1GQUxTRX0KICBkZiA8LSB0aWJibGUoCiAgICBQSUIgPSBjKDEuNywgMS4yLCAyLjUsIDIuOCwgMy42LCAyLjIsIDAuOCwgMS41LCAyLjQsIDUuOSksCiAgICBlbWlzc8O1ZXMgPSBjKAogICAgICA1NTIuNiwgNDYyLjMsIDQ3NS40LCAzNzQuMywgNzQ4LjUsIDQwMC45LCAyNTMuMCwgMzE4LjYsIDQ5Ni44LCAxMTgwLjYKICAgICkKICApCiAgCiAgZGYKICBgYGAKCiogR3LDoWZpY286CgogICAgYGBge3J9CiAgICBkZiAlPiUgCiAgICAgIGdncGxvdChhZXMoUElCLCBlbWlzc8O1ZXMpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICAgICAgICAnZW1pc3PDtWVzXG4obWlsaMO1ZXMgZGVcbnRvbmVsYWRhcyknLAogICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDEyMDAsIDIwMCksCiAgICAgICAgICBsaW1pdHMgPSBjKDAsIDEyMDApCiAgICAgICAgKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKAogICAgICAgICAgJ1BJQiAodHJpbGjDtWVzIFVTJCknLAogICAgICAgICAgYnJlYWtzID0gMDo2LAogICAgICAgICAgbGltaXRzID0gYygwLCA2KQogICAgICAgICkKICAgIGBgYAoKKiBPIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBbYW1vc3RyYWxdey5obH0gw6kKCiAgICBgYGB7cn0KICAgIGNvcihkZiRQSUIsIGRmJGVtaXNzw7VlcykKICAgIGBgYAoKKiBDb21vIGVzdGEgw6kgdW1hIGFtb3N0cmEsIGRldmVtb3MgY2FsY3VsYXIgdW0gaW50ZXJ2YWxvIGRlIGNvbmZpYW7Dp2EgcGFyYSBvIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBbcG9wdWxhY2lvbmFsXXsuaGx9LgoKKiBPIFIgZmF6IGlzdG8gY29tIGEgZnVuw6fDo28gYGNvci50ZXN0YDoKCiAgICBgYGB7cn0KICAgIGN0IDwtIGNvci50ZXN0KGRmJFBJQiwgZGYkZW1pc3PDtWVzKQogICAgY3QKICAgIGBgYAoKKiBDb25jbHVzw6NvOiBjb20gJDk1XCUkIGRlIGNvbmZpYW7Dp2EsIGVzdGltYW1vcyBxdWUgbyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gW3BvcHVsYWNpb25hbF17LmhsfSDDqSBjYXB0dXJhZG8gcGVsbyBpbnRlcnZhbG8KCiAgJCQKICBbXHF1YWQgYHIgY3QkY29uZi5pbnRbMV1gIFxxdWFkIDtccXVhZCAgYHIgY3QkY29uZi5pbnRbMl1gIFxxdWFkXQogICQkCgojIyBFeGVyY8OtY2lvcwoKKiBRdWFsIMOpIG8gdGlwbyBkZSB0ZXN0ZSB1c2FkbyBwb3IgYGNvci50ZXN0YD8KCiogUXVhaXMgc8OjbyBhcyBoaXDDs3Rlc2VzIGRvIHRlc3RlPwoKKiBDb21vIMOpIGNhbGN1bGFkbyBvIHZhbG9yIGRhIGVzdGF0w61zdGljYSBkbyB0ZXN0ZSAoJGByIGN0JHN0YXRpc3RpYyAlPiUgcm91bmQoNClgJCBubyBleGVtcGxvIGFjaW1hKT8KCiogQ29tbyDDqSBjYWxjdWxhZG8gbyB2YWxvciAkcCQgKCRgciBjdCRwLnZhbHVlICU+JSByb3VuZCg3KWAkIG5vIGV4ZW1wbG8gYWNpbWEpPwoKKiBJbXBsZW1lbnRlIHVtYSBmdW7Dp8OjbyBlbSBSIHBhcmEgZmF6ZXIgbyBtZXNtbyBxdWUgYGNvci50ZXN0YC4KCgojIFRyYW5zZm9ybWHDp8O1ZXMKCiMjIEV4ZW1wbG86IGZvdG9ncmFmaWEKCiogQW8gY29uZmlndXJhciB1bWEgY8OibWVyYSBwYXJhIHRpcmFyIHVtYSBmb3RvLCB2b2PDqiBkZXZlIGFqdXN0YXIgYSB2ZWxvY2lkYWRlIGRvIG9idHVyYWRvciBlIGEgYWJlcnR1cmEgZG8gZGlhZnJhZ21hLgoKKiBPIGZhYnJpY2FudGUgZGUgdW1hIGPDom1lcmEgcmVjb21lbmRhIG9zIHNlZ3VpbnRlcyB2YWxvcmVzOgoKICBgYGB7ciBlY2hvPUZBTFNFfQogIGRmIDwtIHRpYmJsZSgKICAgIHZlbG9jaWRhZGUgPSBjKAogICAgICAxIC8gMTAwMCwKICAgICAgMSAvIDUwMCwKICAgICAgMSAvIDI1MCwKICAgICAgMSAvIDEyNSwKICAgICAgMSAvIDYwLAogICAgICAxIC8gMzAsCiAgICAgIDEgLyAxNSwKICAgICAgMSAvIDgKICAgICksCiAgICBhYmVydHVyYSA9IGMoMi44LCA0LCA1LjYsIDgsIDExLCAxNiwgMjIsIDMyKQogICkKICAKICBkZgogIGBgYAoKKiBDb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo286CgogICAgYGBge3J9CiAgICBjb3IoZGYkdmVsb2NpZGFkZSwgZGYkYWJlcnR1cmEpCiAgICBgYGAKCiogR3LDoWZpY286CgogICAgYGBge3J9CiAgICBkZiAlPiUgCiAgICAgIGdncGxvdChhZXModmVsb2NpZGFkZSwgYWJlcnR1cmEpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpCiAgICBgYGAKCiogVmFtb3MgdHJhbnNmb3JtYXIgb3MgdmFsb3JlcyBkZSB1bWEgZGFzIHZhcmnDoXZlaXMgcGFyYSB0b3JuYXIgYSBjb3JyZWxhw6fDo28gbWFpcyBsaW5lYXIgKG1lbm9zIGN1cnZhKToKCiAgICBgYGB7cn0KICAgIGRmIDwtIGRmICU+JSAKICAgICAgbXV0YXRlKHF1YWRfYWJlcnR1cmEgPSBhYmVydHVyYV4yKQogICAgYGBgCiAgICAKICAgIGBgYHtyfQogICAgZGYgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHZlbG9jaWRhZGUsIHF1YWRfYWJlcnR1cmEpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBsYWJzKHkgPSAnYWJlcnR1cmHCsicpCiAgICBgYGAKCiogQSBjb3JyZWxhw6fDo28gc2UgdG9ybmEKCiAgICBgYGB7cn0KICAgIGNvcihkZiR2ZWxvY2lkYWRlLCBkZiRxdWFkX2FiZXJ0dXJhKQogICAgYGBgCgojIyBGdW7Dp8O1ZXMgdXNhZGFzIGVtIHRyYW5zZm9ybWHDp8O1ZXMKCiogJGYoeCkgPSB4XjIkOiDDunRpbCBxdWFuZG8gb3MgdmFsb3JlcyBvcmlnaW5haXMgdMOqbSBjYXVkYSBsb25nYSDDoCBlc3F1ZXJkYSwgb3UgcXVhbmRvIG9zIHZhbG9yZXMgb3JpZ2luYWlzIHTDqm0gY29uY2F2aWRhZGUgcGFyYSBiYWl4by4KCiogJGYoeCkgPSBcc3FydHt4fSQ6IMO6dGlsIHBhcmEgdmFsb3JlcyBxdWUgcmVwcmVzZW50YW0gY29udGFnZW5zLgoKKiAkZih4KSA9IFxsb2cgeCQ6IMO6dGlsIHBhcmEgdmFsb3JlcyBwb3NpdGl2b3MsIHF1ZSBjcmVzY2VtIGRlIGFjb3JkbyBjb20gcGVyY2VudGFnZW5zLCBjb21vIHNhbMOhcmlvcywgcG9wdWxhw6fDtWVzIGV0Yy4gQ29tbyAkXGxvZyAwJCBuw6NvIMOpIGRlZmluaWRvLCB2b2PDqiBwb2RlIHByZWNpc2FyIGFjcmVzY2VudGFyIHVtYSBjb25zdGFudGUgJFx2YXJlcHNpbG9uJCBhb3MgdmFsb3JlcyBhbnRlcyBkYSB0cmFuc2Zvcm1hw6fDo28uCgoqICRmKHgpID0gLVxmcmFjMXtcc3FydHt4fX0kOiBvIHNpbmFsIG5lZ2F0aXZvIG1hbnTDqW0gYSBvcmRlbmHDp8OjbyBkb3MgdmFsb3Jlcy4KCiogJGYoeCkgPSAtXGZyYWMxeCQ6IMO6dGlsIHBhcmEgcmF6w7VlcywgY29tbyBrbS9oLiBPIHNpbmFsIG5lZ2F0aXZvIG1hbnTDqW0gYSBvcmRlbmHDp8OjbyBkb3MgdmFsb3Jlcy4gQ29tbyAkeS8wJCBuw6NvIMOpIGRlZmluaWRvLCB2b2PDqiBwb2RlIHByZWNpc2FyIGFjcmVzY2VudGFyIHVtYSBjb25zdGFudGUgJFx2YXJlcHNpbG9uJCBhb3MgdmFsb3JlcyBhbnRlcyBkYSB0cmFuc2Zvcm1hw6fDo28uCgoKIyMgRXhlcmPDrWNpbwoKKiBBcGxpcXVlIGFzIG91dHJhcyB0cmFuc2Zvcm1hw6fDtWVzIGRhIGxpc3RhIGFjaW1hIGFvcyB2YWxvcmVzIGRhIHZhcmnDoXZlbCBgYWJlcnR1cmFgLiBEZXNlbmhlIGdyw6FmaWNvcywgY2FsY3VsZSBjb3JyZWxhw6fDtWVzLCBlIGNvbXBhcmUgb3MgcmVzdWx0YWRvcy4KCgojIyBFeGVtcGxvOiBwbGFuZXRhcwoKKiBBIGRpc3TDom5jaWEgbcOpZGlhIGRlIHVtIHBsYW5ldGEgYW8gU29sLCBlbSBtaWxow7VlcyBkZSBrbSwgZXN0w6EgY29ycmVsYWNpb25hZGEgY29tIGEgcG9zacOnw6NvIGRvIHBsYW5ldGEgbmEgc2VxdcOqbmNpYSwgbWFzIGNvbW8/CgogIGBgYHtyIGVjaG89RkFMU0V9CiAgcGxhbmV0YXMgPC0gdHJpYmJsZSgKICAgIH5ub21lLCB+cG9zacOnw6NvLCB+ZGlzdMOibmNpYSwKICAgICdNZXJjw7pyaW8nLCAxLCAzNiwKICAgICdWw6pudXMnLCAyLCA2NywKICAgICdUZXJyYScsIDMsIDkzLAogICAgJ01hcnRlJywgNCwgMTQyLAogICAgJ0rDunBpdGVyJywgNSwgNDg0LAogICAgJ1NhdHVybm8nLCA2LCA4ODcsCiAgICAnVXJhbm8nLCA3LCAxNzg0LAogICAgJ05ldHVubycsIDgsIDI3OTYsCiAgICAnUGx1dMOjbycsIDksIDM2NjYKICApICU+JSAKICAgIG11dGF0ZSgKICAgICAgZGlzdMOibmNpYSA9IGRpc3TDom5jaWEgKiAxLjYKICAgICkKICAKICBwbGFuZXRhcwogIGBgYAoKKiBDb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo286CgogICAgYGBge3J9CiAgICBjb3IocGxhbmV0YXMkcG9zacOnw6NvLCBwbGFuZXRhcyRkaXN0w6JuY2lhKQogICAgYGBgCgoqIEdyw6FmaWNvOgoKICAgIGBgYHtyfQogICAgcGxhbmV0YXMgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHBvc2nDp8OjbywgZGlzdMOibmNpYSkpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjkpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeSA9ICdkaXN0w6JuY2lhXG4obWlsaMO1ZXMga20pJwogICAgICAgICkKICAgIGBgYAoKKiBWYW1vcyB0cmFuc2Zvcm1hciBvcyB2YWxvcmVzIGRhcyBkaXN0w6JuY2lhczoKCiAgICBgYGB7cn0KICAgIHBsYW5ldGFzIDwtIHBsYW5ldGFzICU+JSAKICAgICAgbXV0YXRlKGxkaXN0ID0gbG9nMTAoZGlzdMOibmNpYSkpCiAgICBgYGAKICAgIAogICAgYGBge3J9CiAgICBwbGFuZXRhcyAlPiUgCiAgICAgIGdncGxvdChhZXMocG9zacOnw6NvLCBsZGlzdCkpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjkpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeSA9ICdsb2cgZGlzdMOibmNpYVxuKG1pbGjDtWVzIGttKScKICAgICAgICApCiAgICBgYGAKCiogQSBjb3JyZWxhw6fDo28gc2UgdG9ybmEKCiAgICBgYGB7cn0KICAgIGNvcihwbGFuZXRhcyRwb3Npw6fDo28sIHBsYW5ldGFzJGxkaXN0KQogICAgYGBgCgoKIyMgRXhlcmPDrWNpbwoKKiBBcGxpcXVlIGFzIG91dHJhcyB0cmFuc2Zvcm1hw6fDtWVzIGRhIGxpc3RhIGFjaW1hIGFvcyB2YWxvcmVzIGRhIHZhcmnDoXZlbCBgZGlzdMOibmNpYWAuIERlc2VuaGUgZ3LDoWZpY29zLCBjYWxjdWxlIGNvcnJlbGHDp8O1ZXMsIGUgY29tcGFyZSBvcyByZXN1bHRhZG9zLgoKCgoKPGRpdiBzdHlsZT0naGVpZ2h0OiAxMDAwcHgnPjwvZGl2Pgo=